package com.talkyun.openx.server.error;

import com.talkyun.openx.error.ExceptionHandler;
import com.talkyun.utils.json.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;

public class PageBuilder {
    private static PageBuilder instance = new PageBuilder();
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private ExceptionHandler errorHandler = new ExceptionHandler();
    private String errorTemplate;

    private PageBuilder() {
        InputStream ins = PageBuilder.class.getResourceAsStream("/webapp/error.html");
        try {
            errorTemplate = readErrorTemplate(ins);
        } catch (IOException e) {
            throw new RuntimeException("Can't read /webapp/error.html", e);
        }
    }

    public static PageBuilder get() {
        return instance;
    }

    public void responseJsonPage(HttpServletRequest request, HttpServletResponse response, Throwable t) {
        try {
            doWriteJsonPage(request, response, t);
        } catch (Throwable x) {
            logger.error("Response json exception!", x);
        }
    }

    private void doWriteJsonPage(HttpServletRequest request, HttpServletResponse response, Throwable t) throws IOException {
        response.setHeader("Content-Type", "text/html;charset=utf-8");
        response.setStatus(500); // single code: 500
        response.getWriter().write(errorHandler.encode(t));
    }

    public void responseErrorPage(HttpServletRequest request, HttpServletResponse response, Throwable t) {
        try {
            doWriteErrorPage(request, response, t);
        } catch (Throwable x) {
            logger.error("Response page exception!", x);
        }
    }

    private void doWriteErrorPage(HttpServletRequest request, HttpServletResponse response, Throwable t) throws IOException {
        ErrorInfo err = new ErrorInfo();
        String msg = t.getMessage();
        if (msg.toLowerCase().startsWith("not found")) {
            err.setStatusCode(404);
        } else {
            err.setStatusCode(500);
        }
        // set root cause
        err.setErrorMessage(t.getMessage());
        err.setErrorStackTrace(exceptionToString(t));

        // set request
        Request req = new Request();
        req.setHost(request.getHeader("Host"));
        req.setMethod(request.getMethod());
        req.setUri(request.getRequestURI());
        req.setQueryString(request.getQueryString());
        req.setClient(request.getRemoteAddr());
        req.setCookie(cookieToString(request.getCookies()));
        if ("GET".equals(request.getMethod().toUpperCase())) {
            req.setBody(request.getQueryString());
        } else {
            req.setBody("...");
        }
        err.setRequest(req);

        String json = JSON.toJsonString(err);
        String page = errorTemplate.replace("${data}", json);

        response.setHeader("Content-Type", "text/html;charset=utf-8");
        response.setStatus(err.getStatusCode());
        response.getWriter().write(page);
    }

    private String readErrorTemplate(InputStream ins) throws IOException {
        byte[] buf = new byte[1024];
        ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
        int num = ins.read(buf);
        while (num != -1) {
            baos.write(buf, 0, num);
            num = ins.read(buf);
        }
        return baos.toString("utf-8");
    }

    private String exceptionToString(Throwable t) {
        StringWriter sw = new StringWriter(512);
        t.printStackTrace(new PrintWriter(sw));
        return sw.toString();
    }

    private String cookieToString(Cookie[] cookies) {
        // first loading
        if (cookies == null || cookies.length == 0) {
            return "";
        }

        StringBuilder sb = new StringBuilder(96);
        for (Cookie cookie : cookies) {
            sb.append(cookie.getName()).append("=").append(cookie.getValue());
            sb.append(";");
        }
        return sb.substring(0, sb.length() - 1);
    }
}